#ifndef CREATOR_H
#define CREATOR_H

#include <cassert>

#include <vector>
#include <map>
#include <algorithm>
#include <string>
#include <list>

#include <iostream>
using std::cout;
using std::endl;
using std::string;
using std::vector;

#include "prot.h"
#include "paging_api.h"

#include "vaddr.h"

enum { OPT_FLAGS, OPT_MODE };

struct Flags {
  u64 flags;
  u64 uflags;
};

class Address {
public:
  Address() : addr_ (), page_(PAGE_UNDEFINED) {};
  Address(int x) : addr_ (1, x), page_(PAGE_UNDEFINED) {};
  
  void setPage(int page) {
    page_ = page;
  }
  int getPage() {
    return page_;
  }
  
  void push(unsigned val) {
    addr_.push_back(val);
  }
  u64 to64() {
    return vaddr(addr_, PM_64);
  }
  
private:
  vector<unsigned> addr_;
  int page_;
};



class PagingCreator {
  std::map<string, int> pageMap;
  
  //pagesFlags.size = autoEntryNumber.size = number of pages
  
  int newPage(u32 mask) {
    masks.push_back(mask);
    
    return masks.size() - 1;
  }

protected:
  std::vector<u32> masks;
  std::map<int, int> options;
  
public:
  int newUnnamedPage(u32 mask = OFFSET_MASK_4KB) {
    return newPage(mask);
  }
  
  int newPage(string name, u32 mask = OFFSET_MASK_4KB) {
    return (0 == pageMap.count(name)) ?
	      pageMap[name] = newPage(mask) : PAGE_UNDEFINED;
  }
  
  int getPage(string name) {
    if (pageMap.count(name) == 0) {
      pageMap[name] = newPage(OFFSET_MASK_4KB);
    }
    
    return pageMap[name];
  }
};


class TestCreator : public PagingCreator {
public:
  void addCommand(test_command c) {
    if (c.cmd == CMD_MAP) {
      if (options[OPT_MODE] == PM_64 && masks[c.page2] == OFFSET_MASK_4KB)
	c.addr_mask = ADDR_MASK_64_4KB;
      else
	exit(111);
    }
    
    commands.push_back(c);
  }
  
  void addMap(int page1, unsigned pos, int page2, Flags flags);
  
  void addFlush() {
    test_command c;
    memset (&c, 0, sizeof(c));
    
    c.cmd = CMD_FLUSH;
    commands.push_back(c);
  }
    
  void addEnd() {
    test_command c;
    memset (&c, 0, sizeof(c));
    
    c.cmd = CMD_END;
    commands.push_back(c);
  }
  
  void addResult(interrupt_info res) {
    results.push_back(res);
  }
  
  void setOption(int opt, int val) {
    options[opt] = val;
  }
  
  int saveTest(const char *fileName);
  int saveResult(const char *fileName);
  
private:
  std::vector<test_command> commands;
  std::vector<interrupt_info> results;
  std::vector<int> affectedPages;
};

#endif /* CREATOR_H */